package com.health.trans.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.health.entity.annotation.CorrelationCheck;
import com.health.es.entity.log.TransformLog;
import com.health.trans.pojo.JpaHelper;
import com.health.trans.util.CacheMap;
import com.health.trans.util.DataSourceUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.persistence.Column;
import javax.persistence.Id;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author nqlz
 */
@Component
public class CorrelationService {

    private static final Logger logger = LoggerFactory.getLogger(CorrelationService.class);
    private static JpaHelper productJpaHelper;


    private static final CacheMap<Class<?>, CorrelationInfo> CORRELATION_CHECK_MAP = new CacheMap<>();

    private static final CacheMap<Class<?>, Set<String>> IDS_MAP = new CacheMap<>();

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class CorrelationBase {
        private String fieldName;
        private String columnName;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private static class CorrelationInfo {
        private String database;
        private String tableName;
        private List<CorrelationBase> baseList;
    }

    private void checkDataSource() {
        if (Objects.isNull(productJpaHelper)) {
            CorrelationService.productJpaHelper = DataSourceUtil.getJpaHelper("dev-s");
        }
    }

    public TransformLog correlation(Object obj) {

        checkDataSource();
        Field[] fields = obj.getClass().getDeclaredFields();
        String haspCode = ReflectUtil.getFieldValue(obj, "hospCode").toString();

        //获取需要关联信息
        CorrelationInfo checkInfo = CORRELATION_CHECK_MAP.get(obj.getClass(), () -> {
            final CorrelationCheck[] annotation = {null};
            List<CorrelationBase> collect = Arrays.stream(fields).filter(i -> i.isAnnotationPresent(CorrelationCheck.class))
                    .map(i -> {
                        if (Objects.isNull(annotation[0])) {
                            annotation[0] = i.getAnnotation(CorrelationCheck.class);
                        }
                        String columnName = i.isAnnotationPresent(Column.class) ? i.getAnnotation(Column.class).name() : i.getName();
                        return new CorrelationBase(i.getName(), columnName);
                    }).collect(Collectors.toList());
            CorrelationInfo correlationInfo;
            if (annotation[0] != null) {
                correlationInfo = new CorrelationInfo(annotation[0].database(), annotation[0].tableName(), collect);
            } else {
                correlationInfo = new CorrelationInfo("", "", collect);
            }
            CORRELATION_CHECK_MAP.put(obj.getClass(), correlationInfo);
            return correlationInfo;
        });

        //没检查到关联信息
        if (CollUtil.isEmpty(checkInfo.getBaseList())) {
            return null;
        }
        StringBuilder source = new StringBuilder();

        //生成关联信息SQL
        String sqlString = getGenerateSql(checkInfo, source, obj);

        //生成Id信息
        String idString = getGenerateId(checkInfo, obj);

        Long count;

        try {
            count = productJpaHelper.count(sqlString);
        } catch (Exception e) {
            count = 0L;
            //return new TransformLog("2", checkInfo.tableName, haspCode, 1L, "查询关联数据失败", JSONObject.toJSONString(obj));
        }

        if (count > 0L) {
            return null;
        }
        if (StrUtil.isNotBlank(idString)) {
            idString = checkInfo.tableName + idString;
        }
        String result = null;
        if (StrUtil.isNotBlank(source)) {
            result = checkInfo.tableName + source;
        }
        return new TransformLog(idString, "1", result, checkInfo.tableName, haspCode, 1L, "未查询到关联数据", JSONObject.toJSONString(obj));

    }

    private static String getGenerateId(CorrelationInfo checkInfo, Object obj) {
        Set<String> strings = IDS_MAP.get(obj.getClass(), () -> {
            Set<String> ids = Arrays.stream(obj.getClass().getDeclaredFields()).filter(i -> i.isAnnotationPresent(Id.class))
                    .map(Field::getName)
                    .collect(Collectors.toSet());
            IDS_MAP.put(obj.getClass(), ids);
            return ids;
        });
        StringBuilder id = new StringBuilder();
        for (String fieldName : strings) {
            String fieldString = getFieldString(fieldName, obj);
            if (!"tb_base_register".equals(checkInfo.tableName)) {
                id.append("_").append(fieldString);
            } else if (!"cardType".equals(fieldName) && !"cardNum".equals(fieldName)) {
                id.append("_").append(fieldString);
            }
        }
        return id.toString();
    }

    private static String getGenerateSql(CorrelationInfo checkInfo, StringBuilder source, Object obj) {
        StringBuilder stringBuilder = new StringBuilder();
        for (CorrelationBase info : checkInfo.getBaseList()) {
            String fieldString = getFieldString(info.fieldName, obj);
            if (stringBuilder.length() == 0) {
                stringBuilder.append(info.columnName).append("='").append(fieldString).append("'");
                continue;
            }
            stringBuilder.append(" and ").append(info.columnName).append("='").append(fieldString).append("'");
            source.append("_").append(info.fieldName);
        }
        return "select 1 from " + checkInfo.database + "." + checkInfo.tableName + " where " + stringBuilder.toString() + " limit 1";
    }

    private static String getFieldString(String key, Object obj) {
        String res = null;
        try {
            res = ReflectUtil.getFieldValue(obj, key).toString();
        } catch (Exception exception) {
            logger.error("获取字段属性错误", exception);
        }
        return res;
    }
}
